package org.mika.asm;

import java.lang.reflect.Method;
import java.util.HashMap;

import net.sf.cglib.asm.ClassWriter;
import net.sf.cglib.asm.Type;
import net.sf.cglib.core.ClassEmitter;
import net.sf.cglib.core.ClassGenerator;
import net.sf.cglib.core.CodeEmitter;
import net.sf.cglib.core.EmitUtils;
import net.sf.cglib.core.GeneratorStrategy;
import net.sf.cglib.core.ReflectUtils;

public class StaticMethodsOpenerGenerator<T,I> implements GeneratorStrategy {
	private Class<T> clazz;
	private Class<I> iface;

	public StaticMethodsOpenerGenerator(Class<T> c, Class<I> i) {
		this.clazz = c; 
		this.iface = i;
	}
	
	public static Object invoke$private$(String m, Object[]args) {
		throw new NotImplementedException("Method invoke$private$ is inaccessible within this context");
	}

	@Override
	public byte[] generate(ClassGenerator cg) throws Exception {
		ClassWriter  writer = new ClassWriter(true);
		ClassEmitter ce = new ClassEmitter(writer);
		
		ASMUtils.generateOpenerHeader (ce, clazz, iface);
		ASMUtils.generateConstructors (ce, clazz);
		ASMUtils.declareHashMap       (ce, MethodUtils.METHODS);		
		MethodUtils.generateStatic    (ce, clazz, iface);
		
		{
			CodeEmitter e = EmitUtils.begin_method(
				ce, ReflectUtils.getMethodInfo(
					StaticMethodsOpenerGenerator.class.getMethod(MethodUtils.INVOKE_METHOD, new Class<?>[] {
						String.class, Object[].class
					})
				)
			);
			
			e.getstatic( ce.getClassType(), MethodUtils.METHODS, Type.getType(HashMap.class) );
			e.load_arg(0);
			
			e.invoke(
				ReflectUtils.getMethodInfo(
					HashMap.class.getMethod("get", new Class[]{ Object.class })
				)
			);
			
			e.checkcast(Type.getType( Method.class ));
			
			e.aconst_null();
			e.load_arg(1);
			
			e.invoke(
				ReflectUtils.getMethodInfo(
					Method.class.getMethod("invoke", new Class[]{ Object.class, Object[].class })
				)
			);
			
			e.return_value();
			e.end_method();
		}
		
		ce.end_class();		
		return writer.toByteArray();
	}

}
